/* ====================================== http 请求  ====================================== */
import axios from 'axios'
//import '@js/crypto-js'
import $config from './config'
import $fn from './fn'
// import qs from 'qs'
/* ====================================== 参数配置  ====================================== */
const _allowEmpetyParam = false			// 允许参数可为空即: { name:'' }
/**
 *  错误提法信息
 *
 * */
const LogInfo = (method, api, param, data, index)=>{
	if($config.env){
		const colors = ['red', 'green', 'orange']
		console.log(`%c ==================================【 ${method} 】【 ${api} 】==================================`, 'color:' + colors[index])
		console.log(' 参数：', param||{})
		console.log(' 数据：', data)
		console.log(`%c ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— end `, 'color:' + colors[index])
	}
}
/**
 *  配置头信息
 *
 * */
const setConfig = ({ type, token, api, upload  })=>{
	const content = ['application/json;charset=utf-8','application/x-www-form-urlencoded','multipart/form-data']
	const contentType = content[$fn.isValid(type) ? type : $config.contentType]
	// 签名验证
	/*
	let time = new Date().getTime();
    let sign = {
    	rest_timestamp:time.toString(),
		rest_sign:CryptoJS.DES.encrypt(time.toString(), CryptoJS.enc.Utf8.parse('__UWILLBE_'), { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}).toString()
    }*/
	const headerToken = $fn.getToken()
	const contentTypeConfig = { 'Content-Type' : contentType }
	const tokenConfig = headerToken ? { 'Authorization': $fn.getToken() } : { }
	const header = token === false ? contentTypeConfig : { ...contentTypeConfig, ...tokenConfig  }
	
	return {
		baseURL: api,
		headers: header,
		timeout: 30000
		//withCredentials : true	// 跨域请求想要带上cookies设置为 true
	}
}
/**
 *  序列化参数 {a:1,b:2} to ?a=1&b=2
 *
 * */
const serializeParam = body => {
	if(!$fn.hasObject(body)) return ''
	let param = body
	let str=''
	for(var i in param){
		if(_allowEmpetyParam){
			str += i + '=' + param[i] + '&'
		}else{
			if($fn.isValid(param[i])){ str += i + '=' + param[i] + '&' }
		}
	}
	if (str.charAt(str.length - 1) === '&'){ 
		str = str.slice(0, str.length - 1)
	}
	if(str){
		str = '?' + str
	}else{
		str = encodeURI(str) 	// encodeURI 不对 [:, /, ;,?] 进行编码
	}
	return str
}

/**
 * 将 body 以函数形式处理
 *
 * */
const manageBody = body => {
	if($fn.isFunction(body)){
		body = body()
	}else if($fn.hasObject(body)){
		for(let i in body){
			let v = body[i]
			if( !$fn.isValid(v) ){
				if(_allowEmpetyParam){
					body[i] = ''
				}else{
					delete body[i]
				}
			}
		}
	}
	return body
}
/**
 *  动态改变数据
 *
 * */
const setData = (_this, value)=>{
	if(_this){
		if($fn.isFunction(_this)){
			_this(data => {
				for(let i in value){
					data[i] = value[i]
				}
				return {...data}
			})
		}else if(_this?.setState){ // 如果是 react 状态组件
			_this.stateState(value)
		}else{
			for(let i in value){
				_this[i] = value[i]
			}
		}
	}
}
/**
 * http 核心封装
 *
 * */
const HttpRequest = (url, param, action, defined) => {
	let UD = defined || {}
	if(!$fn.isString(url) || !$fn.isValid(url)) { return $fn.toast('接口未传递正确') }
	let body = manageBody(param)
	// if( UD.type === 1) { body = qs?.stringify?.(body) }
	
	const headerConfig = setConfig({
		type	: UD.type,
		token	: UD.token,
		api		: url.indexOf('http') !== -1 ? url : $config.api()
	})
	
	UD?.onStart?.()		// 一开始就调用
	url = UD.id ? url + '/' + UD.id : url 
	const m = {
		'get' 		: () => axios.get( url + serializeParam(body), headerConfig),
		'delete' 	: () => axios.delete(url, headerConfig),
		'post' 		: () => axios.post(url, body, headerConfig),
		'put' 		: () => axios.put(url, body, headerConfig),
	}
	const promise = m[action]()
	// 加载效果
	return new Promise((resolve, reject) => {
		promise.then(res => {
			const resData = res.data;
			const { code, data, msg, status } = resData;
			if(code === 200 || status === 1){
				resolve(data);
				LogInfo(action, url, param, data, 1);
			} else if(code === 200 || code === 201 || code === 401){	// 未登录
				$fn.toast( code === 200 || code === 401  ? '请先登录' : '登录信息已过期，请重新登录!')
				$fn.remove('user')
				$fn.recordSkip()
				setTimeout(() => $fn.go('/login'), 200 )// 跳转不同登录页
			}else{ // 数据请求成功但不符合规则
				reject(data);
				if(UD.onMsg){
					UD?.onMsg?.(data)		// 自定义提示
					UD?.onError?.(data)
				}else{
					$fn.toast(msg, UD.onError, 30000)			// 默认开启出错提示
				}
				
				UD?.onFail?.(resData)	// 数据处理不满足条件时调用
				
				LogInfo(action, url, param, data, 2)
			}
			
			UD?.onEnd?.(resData)  		// 只要调用接口就调用
			UD?.onSuccess?.(resData) 	// 只要调用接口成功就调用
		}, (err) => { 					// 接口错误处理
			if(!UD.noError){ $fn.toast('服务器或网络出错')}
			UD?.onNet?.()				// 服务器出错或无网络调用
			UD?.onError?.()				// 只要出错就调用
			UD?.onEnd?.()  				// 只要调用接口就调用
			LogInfo(action, url, param, '无', 0)
		})
	})
}
/**
 * post 提交
 *
 * */
const post = (_this,api,option)=>{
	let opt = {
		param			: { },
		loading 		: true,
		loadingText		: '数据提交中',		
		loadingName		: 'postLoading',
		method			: 'post',
		type			: $config.contentType, 	// Content-Type 类型
		...option
	}
	
	setData(_this,{ [opt.loadingName]:true, loading:true })
	
	if(opt.loading){
		
		if(opt.type === 2){ opt.loadingText = '图片上传中' }
		
		$fn.loading(true,opt.loadingText + '...')
	}
	
	const run = ()=>{ opt.onSuccess && opt.onSuccess() }
	
	return new Promise((resolve, reject)=>{
		HttpRequest(api, { ...opt.param, ..._this?.model, ..._this?.ids  }, opt.method, {
			onStart	: ()=>{ opt?.onStart?.(true) },
			onEnd	: ()=>{
				setData(_this,{ [opt.loadingName]:false, loading:false })
				opt.loading && $fn.loading(false)
				opt.onEnd && opt.onEnd(false)
			},
			onMsg	: opt.onMsg && ( data => { $fn.isFunction(opt.onMsg) && opt.onMsg(data) }),
			noError	: opt.noError,
			onError	: opt.onError,
			onFail	: opt.onFail,
			token	: opt.token,
			isBody	: opt.isBody,
			type	: opt.type
		}).then(data=>{
			resolve(data)
			// 提示后执行
			if(opt.successText){
				if(opt.runFirst){
					$fn.toast(opt.successText);
					run()
				}else{
					$fn.toast(opt.successText,run)
				}
			}else{ // 直接执行
				run()
			}
		},data=>{
			reject(data)
		})
	})
}
const del = (_this,api,option) => post(_this, api, { method:'delete', loadingText:'数据删除中', ...option })
const put = (_this,api,option) => post(_this, api, { method:'put', loadingText:'数据提交中', ...option })
/**
 * get 获取数据
 *
 * */
const pull = (_this,api,option)=>{
	let opt = {
		param		: {  },		// 参数
		dataName	: 'data',
		loading		: false,					// 如果有加载效果
		loadingText	: '数据加载中',			
		loadingName : 'pullLoading',
		...option
	}
	
	if(_this){
		if(opt.first && !_this.first){
			if(opt.loading){ $fn.loading(true,opt.loadingText + '...') }
			setData(_this,{ [opt.loadingName]:true, loading:true, first: true })
		}else{
			setData(_this,{ [opt.loadingName]:true, loading:true })
		}
		_this?.onLoading?.(_this.loading)
	}
	return new Promise((resolve,reject)=>{
		HttpRequest(api, opt.param , 'get', {
			id 			: opt.id,
			onStart		: ()=>{ opt?.onStart?.(true) },
			onEnd		: ()=>{
				setData(_this,{ [opt.loadingName]:false, loading:false })
				opt.loading && $fn.loading(false)
				opt.onEnd && opt.onEnd(false)
				_this?.onLoading?.(_this.loading)
			},
			onMsg	: opt.onMsg && ( data => { $fn.isFunction(opt.onMsg) && opt.onMsg(data) }),
			noError	: opt.noError,
			onError	: ()=>{
				opt?.onError?.()
				opt.loading && $fn.loading(false)
			},
			token	: opt.token,
		}).then(data =>{
			let result = data?.rows ? data.rows : data
			if($fn.isFunction(opt.onSuccess)){ result = opt.onSuccess(result) }
			if(!opt.paging){
				setData(_this,{ [opt.dataName]: result })
				resolve(result)
			}else{
				resolve(data)
			}
		})
	})
}
/**
 * 分页获取数据
 *
 * */
const paging = (_this,api,option)=>{
	let opt = {
		param		: { },					// 参数
		loading		: true,					// 如果有加载效果
		loadingName	: 'pagingLoading',
		dataName	: 'data',
		paging		: true, 
		...option
	}
	
	Object.assign(opt,option || {});
	
	const { $config } = window
	
	_this.model = { ..._this.model, ...opt.param }
	_this.query = { ..._this.query, ...opt.query }
	
	const model = _this.model
	
	let param = model
	
	if(opt.paging !== 0){
		const { page, size } = param
		param = {
			page : page || 1, 		// 当前页
			size : size || $config.pageSize,		// 每页显示多少条数据
			..._this.query,
			...model,
		}
		param['pageNum'] = param.page
		param['pageSize'] = param.size
		delete param.size
		delete param.page
	}
	
	return new Promise((resolve)=>{
		pull(_this,api,{
			onStart		: ()=>{ opt?.onStart?.(true) },
			onEnd		: ()=>{ opt?.onEnd?.(true) },
			onError		: opt.onError,
			loading		: opt.loading,
			loadingName : opt.loadingName,
			paging 		: true,
			param,
		}).then( resData =>{
			if(opt.paging !== 0){
				let { rows, data, pageNum, pageSize, pageTotal, total } = resData || {}
				data = data || rows
				if(opt.setFields){ data = data.map( v => ({ ...v, ...opt.setFields(v) }) ) } 		// 自定义数据字段
				if(_this){
					setData(_this,{
						[opt.dataName]: data,
						pager : {
							...resData,
							data 		: data || [],
							page		: pageNum, 		// 当前页码
							size 		: pageSize, 	// 每页多少条数据
							total_items	: total,		// 总共多少条数据
							total_pages	: pageTotal,	// 总共多少页
						}
					})
				}
				resolve({ data:resData, rows: data })
			}else{
				setData(_this,{ [opt.dataName]: resData })
				resolve({ data:resData })
			}
		})
	})
}
/**
 * 暴露方法
 *
 * */
const Index = { post, del, put, pull, paging }
export default Index